package com.corn.juc.valatile;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * @author : Jim Wu
 * @version 1.0
 * @function :
 * @since : 2020/12/14 17:34
 */

public class VolatileTest1 {

    public static void main(String[] args) throws Exception {
        VolatileDemo volatileDemo = new VolatileDemo();
        // 可见性测试
//        testVisiable(volatileDemo);
        // 测试不保证原子性
        testAtomic();

    }

    /**
     * 可见性实验
     *
     * @param volatileDemo
     */
    private static void testVisiable(VolatileDemo volatileDemo) {
        new Thread(() -> {
            try {
                // 模拟耗时操作
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            volatileDemo.num = 100;
        }).start();
        System.out.println("修改数字线程执行完毕...");
        // 不加volatile之前 上面更新了 num的时并没有通知主线程导致死循环
        while (volatileDemo.num == 0) {

        }
        System.out.println(volatileDemo.num);
        System.out.println("Main线程结束");
    }

    /**
     * 验证不保证原子性
     * 1. 想要解决原子性可以使用 Atomic包装的类
     * 2. 使用 synchronized 关键字同步
     * 3. 使用lock锁
     */
    public static void testAtomic() throws InterruptedException {
        VolatileDemo demo = new VolatileDemo();
        /**
         * 10 个线程 每个线程+1000次 demo.num的值应该是10*1000才正确
         */
        IntStream.range(0, 10).forEach(i -> {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.num++;
                    demo.atomicI.incrementAndGet();
                }
            }).start();
        });
        // 当活动线程<2 说明上面创建的线程都执行完毕了 2个线程分别是 main线程和gc线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        // 普通字段 即是加了volatile也不保证原子性
        System.out.println(demo.num);
        // Atomic类包装类即是没有加volatile也保证原子性
        System.out.println(demo.atomicI.get());

    }
}

class VolatileDemo {
    volatile int num = 0;

    AtomicInteger atomicI = new AtomicInteger(0);

    int a = 0;

    boolean flag = false;

    public void method1() {
        a = 1;
        flag = true;
    }

    public void method2() {
        if (flag) {
            a = a + 1;
            System.out.println(a);
        }
    }
}